我們知道若元件拆分得越細,可以在其他元件內的重複組合使用的頻率就越高,所以會希望 CSS 只跟隨著元件,當這個元件被拿去其他元件使用時,也不會因為要考慮樣式的問題,而造成不易重複使用。
那在 CSS 的結構設計上,我們要怎麼處理呢?主要可以分成以下幾種方式。
接下來就來簡單說明這四種處理方式。
假設我們有二個元件 - ComponentA、ComponentB
我們可以在引入的樣式檔案加上 namespaces 標示為不同元件的樣式
.ComponentA.title { color: red; }
.ComponentA .xxx {...}
.ComponentB.title { font-size: 40px; }
.ComponentB .xxx {...}
<div class="ComponentA">
<h1 class="title">元件A</h1>
</div>
<div class="ComponentB">
<h1 class="title">元件B</h1>
</div>
不過若 ComponentB 是 ComponentA 的子元件時,可能會發生 ComponentA 的樣式作用在了 ComponentB 上。
<div class="ComponentA">
<h1 class="title">元件A</h1>
<!-- ComponentB 是 ComponentA 的子元件 -->
<div class="ComponentB">
<h1 class="title">元件B</h1>
</div>
</div>
改善的方法可以遵循 BEM (Block, Element, Modifier) 的規範來設計。
.ComponentA__title { color: red; }
.ComponentB__title { font-size: 40px; }
<div class="ComponentA">
<h1 class="ComponentA__title">元件A</h1>
<!-- ComponentB 是 ComponentA 的子元件 -->
<div class="ComponentB">
<h1 class="ComponentB__title">元件B</h1>
</div>
</div>
但是在多人維護的專案中使用 BEM 規範約定的方式來解決 CSS,開發者必須全盤深入理解專案的樣式設計結構,才有能力去組裝元件不影響樣式。
透過把 CSS 寫在 JS 中,可以確保特定樣式只會作用在該元件,而且也可把 JS 中的一些邏輯判斷放到 CSS 使用。
React 有多第三方可用的 CSS-in-JS 套件,其中 styled-components 及 emotion 都很廣泛被使用。因為篇幅的關係這裡只介紹 styled-components
。
官網:https://styled-components.com/
npm install styled-components
import styled from 'styled-components';
const styled元件 = styled.你要用的DOM元素`css程式碼`
...
// 在 jsx 使用時
<styled元件>內容1</styled元件>
<styled元件>內容2</styled元件>
styled元件會在「指定的DOM元素」上,產生一個 hash 編碼的 class
<h1 class="sc-ftTHYK gmEgew">...</h1>
<h1 class="sc-ftTHYK gmEgew">...</h1>
相同的styled元件會產生同樣,且不與其他元件重複的class
// components/ComponentA.js
import styled from "styled-components";
const Title = styled.h1`
font-size: 40px;
color: red;
`;
const ComponentA = (props) => {
return (
<>
<Title>{props.text}</Title>
{props.children}
</>
);
};
export default ComponentA;
// components/ComponentB.js
import styled from "styled-components";
const Title = styled.h1`
color: blue;
`;
const ComponentB = (props) => {
return <Title>{props.text}</Title>;
};
export default ComponentB;
// App.js
import ComponentA from "./components/ComponentA";
import ComponentB from "./components/ComponentB";
export default function App() {
return (
<div>
<ComponentA text="ComponentA">
<ComponentB text="ComponentB-1" />
<ComponentB text="ComponentB-2" />
</ComponentA>
</div>
);
}
執行結果:https://codesandbox.io/s/styled-components-04ccsy
// compontents/Title.js
import styled from "styled-components";
const BaseTitle = styled.h1`
color: ${(props) => (props.color ? props.color : "black")};
`;
const Title = styled(BaseTitle)`
font-size: 30px;
`;
export default Title;
// components/ComponentA.js
import Title from "./Title";
const ComponentA = (props) => {
return (
<>
<Title color="green">{props.text}</Title>
{props.children}
</>
);
};
export default ComponentA;
// components/ComponentB.js
import Title from "./Title";
const ComponentB = (props) => {
return <Title color="purple">{props.text}</Title>;
};
export default ComponentB;
執行結果:https://codesandbox.io/s/styled-components-props-m8wlnk
雖然使用相同的 Title 元件,但因為它會在不同元件產生不同的 hash class,因此可以避免在不同地方使用到相同 css selector 而互相影響
官網:https://github.com/css-modules/css-modules
使用 npx create-react-app 建立專案的同時,默認就支持 css-module
使用方式如下:
變數
形式導入 CSS 文件import styles from './[name].module.css'
變數.在css檔定義好的class
className={styles.title}
因為在 CodeSandBox 無法實現,所以大家可以使用 create-react-app
自行建立專案來實作。
// App.js
import ComponentA from "./components/ComponentA";
import ComponentB from "./components/ComponentB";
const App = () => {
return (
<div>
<ComponentA text="A">
<ComponentB text="B"></ComponentB>
</ComponentA>
</div>
);
}
export default App;
// components/ComponentA.js
import styles from "./ComponentA.module.css";
const ComponentA = (props) => {
return (
<>
<h1 className={styles.textColor}>
<span>Component</span>
<span>{props.text}</span>
</h1>
{props.children}
</>
);
};
export default ComponentA;
// components/ComponentA.module.css
.textColor {
color: red;
}
// components/ComponentB.js
import styles from "./ComponentB.module.css";
const ComponentB = (props) => {
return (
<>
<h1 className={styles.textColor}>
<span>Component</span>
<span>{props.text}</span>
</h1>
</>
);
};
export default ComponentB;
// components/ComponentB.module.css
.textColor {
color: blue;
}
css-module 會在 class 後面添加 hash 值,所以就算我們二個樣式檔案都使用相同名稱(eg. textColor)也不會互相衝突,因為經過 css-module 編譯過後會將 class 轉成一個唯一沒有重複的 class
css-module 允許使用 :global(.className)
的語法,這樣宣告出來的 class 就是 global,而不會被編譯成 hash class。
:global(.bgYellow) {
background-color: yellow;
}
- <span>Component</span>
+ <span className="bgYellow">Component</span>
utility-first CSS 把大致上 CSS 會用到的屬性都用單個 class 來表示。
.m-1 {
margin: 0.25rem;
}
.w-16 {
width: 16px;
}
.text-black {
color: rgb(0 0 0);
}
直接把 utility class 加在你想要作用的 element 上,大部分的情況下可以不需要自己編寫樣式,於是你就可以非常迅速地把網站的各種部分建構出來,也可以很輕鬆地調整。
用這樣的寫法,就很像在元件上使用 inline-style,那麼元件無論怎麼搬動,都不用擔心樣式的問題。
而且使用 class 而不是 inline-style,好處就是當你有特定的 class,要有客製化的需求,那麼你只要在設定檔調整好該 class 的定義即可,而不同去每個元件找到相同的 style 去做調整。
這邊使用 Tailwind 的語法來示意
所以若想要在不同裝置的尺寸改變元素的寬度,只要簡單的設置如下
<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">
這邊使用 Tailwind 來示意
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
如果專案有需要調整樣式的需求,可以在這邊做調整
@tailwind base;
@tailwind components;
@tailwind utilities;
// 如果專案的需求是改變 h1 的預設大小,使用 @layer base,再做更改
@layer base {
h1 {
@apply text-6xl text-red-500 my-5;
}
}
export default function App() {
return (
<div className="text-3xl font-bold underline">
Hello world!
</div>
)
}
當專案越來越複雜時,個人非常推薦使用 utility-first CSS Framework 來做為 React 元件的開發。它能讓樣式跟著元件,也維持著 class 是可以很容易做調整的特性。在現代幾乎都要做 RWD 的網站專案下,因為它提供了直觀又好懂的 RWD 斷點寫法,也容易做出重用性很高且同時符合 RWD 的元件。
若想要深入理解 utility-first CSS Framework,尤其是 TailwindCSS 的話,千萬不要錯過 TailwindCSS - 從零開始,這個鐵人賽系列文。
在此之前,會發現到目前都還只有做單一頁面的元件組合。但是在真實世界當中,專案是由很多頁面組合而成。通常可以按下導覧列的連結,轉導至指定的頁面。這就是接下來要介紹 React Router 的功能,
https://www.infoq.cn/article/ftlppdefo27prgqhlo5a
https://ithelp.ithome.com.tw/articles/10223071
https://ithelp.ithome.com.tw/articles/10240466
https://5xruby.tw/posts/tailwind-css-plugin
https://tailwindcss.com/docs/guides/create-react-app